#include "act_compare.hpp"
#include "../action_constants.hpp"
#include "../transaction.hpp"

using etcdserverpb::Compare;

namespace etcdv3 {

//-----------------------------------------------------------------------------
AsyncCompareAndDeleteAction::AsyncCompareAndDeleteAction(const ActionParameters& param, Atomicity_Type type)
    : Action(param) {
    Transaction transaction(parameters.key);
    if (type == Atomicity_Type::PREV_VALUE) {
        transaction.init_compare(parameters.old_value, Compare::CompareResult::Compare_CompareResult_EQUAL,
                                 Compare::CompareTarget::Compare_CompareTarget_VALUE);
    } else if (type == Atomicity_Type::PREV_INDEX) {
        transaction.init_compare(parameters.old_revision, Compare::CompareResult::Compare_CompareResult_EQUAL,
                                 Compare::CompareTarget::Compare_CompareTarget_MOD);
    }

    transaction.setup_compare_and_delete_operation(parameters.key);
    transaction.setup_basic_failure_operation(parameters.key);

    context.set_deadline(parameters.gainDeadline());
    reader.reset(parameters.kv_stub->AsyncTxn(&context, transaction.txn_request, &cq_).release());

    reader->Finish(&reply, &status, (void*)this);
}

AsyncTxnResponse AsyncCompareAndDeleteAction::ParseResponse() {
    AsyncTxnResponse txn_resp;
    if (!status.ok()) {
        txn_resp.set_error_code(status.error_code());
        txn_resp.set_error_message(status.error_message());
    } else {
        txn_resp.ParseResponse(parameters.key, parameters.withPrefix, reply);
        txn_resp.set_action(COMPAREDELETE_ACTION);

        if (!reply.succeeded()) {
            txn_resp.set_error_code(101);
            txn_resp.set_error_message("Compare failed");
        }
    }

    return txn_resp;
}

//-----------------------------------------------------------------------------
AsyncCompareAndSwapAction::AsyncCompareAndSwapAction(const ActionParameters& param, Atomicity_Type type)
    : Action(param) {
    Transaction transaction(parameters.key);
    if (type == Atomicity_Type::PREV_VALUE) {
        transaction.init_compare(parameters.old_value, Compare::CompareResult::Compare_CompareResult_EQUAL,
                                 Compare::CompareTarget::Compare_CompareTarget_VALUE);
    } else if (type == Atomicity_Type::PREV_INDEX) {
        transaction.init_compare(parameters.old_revision, Compare::CompareResult::Compare_CompareResult_EQUAL,
                                 Compare::CompareTarget::Compare_CompareTarget_MOD);
    }

    transaction.setup_basic_failure_operation(parameters.key);
    transaction.setup_compare_and_swap_sequence(parameters.value, parameters.lease_id);

    context.set_deadline(parameters.gainDeadline());
    reader.reset(parameters.kv_stub->AsyncTxn(&context, transaction.txn_request, &cq_).release());

    reader->Finish(&reply, &status, (void*)this);
}

AsyncTxnResponse AsyncCompareAndSwapAction::ParseResponse() {
    AsyncTxnResponse txn_resp;

    if (!status.ok()) {
        txn_resp.set_error_code(status.error_code());
        txn_resp.set_error_message(status.error_message());
    } else {
        txn_resp.ParseResponse(parameters.key, parameters.withPrefix, reply);
        txn_resp.set_action(COMPARESWAP_ACTION);

        //if there is an error code returned by parseResponse, we must
        //not overwrite it.
        if (!reply.succeeded() && !txn_resp.get_error_code()) {
            txn_resp.set_error_code(101);
            txn_resp.set_error_message("Compare failed");
        }
    }

    return txn_resp;
}

}
